package com.zyh.shortlick.admin.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zyh.shortlick.admin.common.biz.user.UserContext;
import com.zyh.shortlick.admin.common.convention.exception.ClientException;
import com.zyh.shortlick.admin.common.convention.result.Result;
import com.zyh.shortlick.admin.dao.entity.GroupDO;
import com.zyh.shortlick.admin.dao.mapper.GroupMapper;
import com.zyh.shortlick.admin.dto.req.ShortLinkGroupSortReqDTO;
import com.zyh.shortlick.admin.dto.req.ShortLinkGroupUpdateReqDTO;
import com.zyh.shortlick.admin.dto.resp.ShortLinkGroupRespDTO;
import com.zyh.shortlick.admin.remote.dto.ShortLinkActualRemoteService;
import com.zyh.shortlick.admin.remote.dto.resp.ShortLinkGroupCountQueryRespDTO;
import com.zyh.shortlick.admin.service.GroupService;
import com.zyh.shortlick.admin.toolkit.RandomGenerator;
import lombok.RequiredArgsConstructor;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Objects;
import java.util.Optional;

import static com.zyh.shortlick.admin.common.constant.RedisCacheConstant.LOCK_GROUP_CREATE_KEY;

@Service
@RequiredArgsConstructor
public class GroupServiceImpl extends ServiceImpl<GroupMapper, GroupDO> implements GroupService {

    private final ShortLinkActualRemoteService shortLinkActualRemoteService;
    private final RedissonClient redissonClient;

    @Value("${short-link.group.max-num}")
    private Integer groupMaxNum;



    //    两个saveGroup，一个是groupController那里来新增group的
//    另外一个是注册时自动save的默认分组
//    注册时没登录获取不了userContext中的username，就需要重载方法
    @Override
    public void saveGroup(String name) {
        saveGroup(UserContext.getUsername(), name);
    }

    //    添加限制，最大添加20个分组
    @Override
    public void saveGroup(String username, String groupName) {
        RLock lock = redissonClient.getLock(String.format(LOCK_GROUP_CREATE_KEY, username));
        lock.lock();
        try {
            LambdaQueryWrapper<GroupDO> lqw = new LambdaQueryWrapper<>();
            lqw.eq(GroupDO::getUsername, username)
                    .eq(GroupDO::getDelFlag, 0);
            List<GroupDO> list = this.list(lqw);
//            超过20个就报错
            if (CollUtil.isNotEmpty(list) && list.size() == groupMaxNum){
                throw new ClientException(String.format("已超出最大分组：%d",groupMaxNum));
            }
            String gid;
            do {
                gid = RandomGenerator.generateRandom();
            } while (hasGid(username, gid));
            GroupDO groupDO = GroupDO.builder()
                    .gid(gid)
                    .name(groupName)
                    .username(username)
                    .sortOrder(0)
                    .build();
            this.save(groupDO);
        }finally {
            lock.unlock();
        }
    }

    //    只讲大体思路，GroupDO里没有要返回的gid的数量，这个数量要通过远程调用获取，然后添加到要返回的实体类
    @Override
    public List<ShortLinkGroupRespDTO> listGroup() {
        LambdaQueryWrapper<GroupDO> lqw = new LambdaQueryWrapper<>();
        lqw.eq(GroupDO::getDelFlag, 0)
                .eq(GroupDO::getUsername, UserContext.getUsername())
                .orderByDesc(GroupDO::getSortOrder, GroupDO::getUpdateTime);
//        获取缺少gid数量的类的列表
        List<GroupDO> list = this.list(lqw);
//        取出获取的列表中gid的数据，远程调用获取gid的数量
        Result<List<ShortLinkGroupCountQueryRespDTO>> listResult = shortLinkActualRemoteService
                .listGroupShortLinkCount(list.stream().map(GroupDO::getGid).toList());
//        把GroupDO的列表转换为最后要返回的实体类的列表
        List<ShortLinkGroupRespDTO> shortLinkGroupRespDTOList = BeanUtil.copyToList(list, ShortLinkGroupRespDTO.class);
//        遍历要返回的类的列表，遍历过程中添加count
        shortLinkGroupRespDTOList.forEach(each -> {
            Optional<ShortLinkGroupCountQueryRespDTO> first = listResult.getData().stream()
                    .filter(item -> Objects.equals(item.getGid(), each.getGid()))
                    .findFirst();
//            有的分组下没有创建短链接，短链接的数量就是0
//            each代表要返回的group类，其中还有一个短链接数量需要从link服务调取
//            item就是调取的结果，存在each中有但是item中没有的gid
//            就是有的分组下没有创建短链接这种情况
//            这种情况下first不存在，不存在就给each的数量值设为0
            first.ifPresentOrElse(item -> each.setShortLinkCount(first.get().getShortLinkCount()), () -> each.setShortLinkCount(0));
        });
        return shortLinkGroupRespDTOList;
    }

    @Override
    public void updateGroup(ShortLinkGroupUpdateReqDTO requestParam) {
        LambdaQueryWrapper<GroupDO> lqw = new LambdaQueryWrapper<>();
        lqw.eq(GroupDO::getGid, requestParam.getGid())
                .eq(GroupDO::getUsername, UserContext.getUsername())
                .eq(GroupDO::getDelFlag, 0);
        GroupDO groupDO = new GroupDO();
        groupDO.setName(requestParam.getName());
        this.update(groupDO, lqw);
    }

    @Override
    public void deleteGroup(String gid) {
        LambdaQueryWrapper<GroupDO> lqw = new LambdaQueryWrapper<>();
        lqw.eq(GroupDO::getGid, gid)
                .eq(GroupDO::getUsername, UserContext.getUsername())
                .eq(GroupDO::getDelFlag, 0);
        GroupDO groupDO = new GroupDO();
        groupDO.setDelFlag(1);
        this.update(groupDO, lqw);
    }

    @Override
    public void sortGroup(List<ShortLinkGroupSortReqDTO> requestParam) {
        requestParam.forEach(each -> {
            GroupDO groupDO = GroupDO.builder()
                    .sortOrder(each.getSortOrder())
                    .build();
            LambdaQueryWrapper<GroupDO> lqw = new LambdaQueryWrapper<>();
            lqw.eq(GroupDO::getUsername, UserContext.getUsername())
                    .eq(GroupDO::getGid, each.getGid())
                    .eq(GroupDO::getDelFlag, 0);
            this.update(groupDO, lqw);
        });
    }

    //    判断gid是否重复，重复需要重新生成,做了唯一索引
    public Boolean hasGid(String username, String gid) {
        LambdaQueryWrapper<GroupDO> lwq = new LambdaQueryWrapper<>();
        lwq.eq(GroupDO::getGid, gid)
//                TODO 设置用户名
                .eq(GroupDO::getUsername, username)
                .eq(GroupDO::getDelFlag, 0);
        GroupDO one = this.getOne(lwq);
        return one != null;
    }
}
